#pragma once

#include "compi/context.h"
#include "compi/result.h"

#include <mpi.h>
#include <optional>
#include <vector>

namespace compi
{
namespace topology
{

struct CartCreateParams
{
    std::vector<int> dims;
    std::vector<int> periods;
    bool reorder = true;
    MPI_Comm base_comm = MPI_COMM_NULL;
};

struct GraphCreateParams
{
    std::vector<int> index; // cumulative degree array
    std::vector<int> edges; // adjacency list
    bool reorder = true;
    MPI_Comm base_comm = MPI_COMM_NULL;
};

class TopologyHandle
{
public:
    TopologyHandle() = default;
    TopologyHandle(Context & ctx, MPI_Comm comm, bool owns = true) noexcept;
    ~TopologyHandle();

    TopologyHandle(const TopologyHandle &) = delete;
    TopologyHandle & operator=(const TopologyHandle &) = delete;

    TopologyHandle(TopologyHandle && other) noexcept;
    TopologyHandle & operator=(TopologyHandle && other) noexcept;

    MPI_Comm comm() const noexcept { return _comm; }
    bool owns() const noexcept { return _owns; }
    bool valid() const noexcept { return _comm != MPI_COMM_NULL; }
    Context * context() const noexcept { return _ctx; }

    MPI_Comm release() noexcept;
    void reset() noexcept;

private:
    Context * _ctx{nullptr};
    MPI_Comm _comm{MPI_COMM_NULL};
    bool _owns{false};
};

Result<TopologyHandle> create_cartesian(Context & ctx, const CartCreateParams & params) noexcept;
Result<TopologyHandle> create_graph(Context & ctx, const GraphCreateParams & params) noexcept;

Result<std::vector<int>> cart_coords(const TopologyHandle & topo, int rank) noexcept;
Result<int> cart_rank(const TopologyHandle & topo, const std::vector<int> & coords) noexcept;

Result<std::vector<int>> graph_neighbors(const TopologyHandle & topo, int max_neighbors = 0) noexcept;

} // namespace topology
} // namespace compi
